﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace XNA_Game_Framework
{
	public class Container : SuperControl
	{
		protected Vector2 size;
		protected List<Container> subwindows;

		#region Properties
		protected Rectangle DrawArea
		{
			get { return new Rectangle((int)this.ScreenPosition.X, (int)this.ScreenPosition.Y, this.Width, this.Height); }
		}

		protected InputHandler InputHandler
		{
			get
			{
				if (Parent is Screen)
				{
					return ((Screen)Parent).InputHandler;
				}
				else if (Parent is Container)
				{
					return ((Container)Parent).InputHandler;
				}
				return null;//Should never happen, as it should be impossible to make Windows on other than Screens and Windows
			}
		}

		/// <summary>
		/// Gets the size of the window
		/// </summary>
		public override Vector2 Size
		{
			get { return size; }
		}

		public List<Container> Subwindows
		{
			get { return subwindows; }
		}

		private ThemeManager Themes
		{
			get { return Screen.Themes; }
		}
		#endregion

		public Container(string name, object parent, Vector2 position, Vector2 size)
			: base(name, parent, position)
		{
			controls = new ControlManager(this);
			this.size = size;

			subwindows = new List<Container>();
		}

		#region AddWindow
		public WindowVar AddWindowVar(string name, string title, Vector2 position, Vector2 size, string theme)
		{
			Theme.WindowVar windowTheme = (Theme.WindowVar)Themes.GetTheme(theme);
			WindowVar newWindow = new WindowVar(name, this, title, position, size, windowTheme);
			subwindows.Add(newWindow);
			return newWindow;
		}

		public Container AddSubwindow(string name, Vector2 position, Vector2 size)
		{
			Container subwindow = new Container(name, this, position, size);
			subwindows.Add(subwindow);
			return subwindow;
		}
		#endregion

		#region AddControl
		/// <summary>
		/// Adds a border control to the container
		/// </summary>
		/// <param name="name">The unique system name of control</param>
		/// <param name="position">Position in the window</param>
		/// <param name="sides">The sides to add borders on</param>
		/// <param name="border">Texture for the border</param>
		/// <returns></returns>
		public Border AddBorder(string name, Vector2 position, Vector2 size, Border.Sides sides, string theme)
		{
			return controls.AddBorder(name, position, size, sides, (Theme.Border)Themes.GetTheme(theme));
		}

		/// <summary>
		/// Adds a button control to the container
		/// <param name="name">The unique system name of control</param>
		/// <param name="position">Position in the window</param>
		/// <param name="text">Text displayed on the button</param>
		/// <param name="font">Font of the text</param>
		/// <param name="buttonTexture">Texture for the button</param>
		/// <returns></returns>
		public Button AddButton(string name, Vector2 position, string theme, string text="")
		{
			Theme.Button buttonTheme = (Theme.Button)Themes.GetTheme(theme);
			if (text != "")
			{
				return controls.AddButton(name, position, buttonTheme, text);
			}
			else
			{
				return controls.AddButton(name, position, buttonTheme, buttonTheme.text);
			}
		}

		/// <summary>
		/// Adds an image to the container
		/// </summary>
		/// <param name="name">The unique system name of control</param>
		/// <param name="position">Position in the window</param>
		/// <param name="image">The image to display</param>
		/// <returns></returns>
		public Image AddImage(string name, Vector2 position, Texture2D image)
		{
			return controls.AddImage(name, position, image);
		}

		/// <summary>
		/// Adds a new label control to the container
		/// </summary>
		/// <param name="name">The unique system name of control</param>
		/// <param name="position">Position in the window</param>
		/// <param name="text">Text of the label</param>
		/// <param name="font">Font of the text</param>
		/// <returns>The label created</returns>
		public Label AddLabel(string name, Vector2 position, string text, SpriteFont font)
		{
			return controls.AddLabel(name, position, text, font);
		}

		/// <summary>
		/// Adds a new menu control to the container
		/// </summary>
		/// <param name="name">The unique system name of control</param>
		/// <param name="position">Position in the window</param>
		/// <param name="entries">Array of entries in the menu, from first to last</param>
		/// <param name="font">Font of the entries</param>
		/// <param name="selectionBox">Texture of the selection marker</param>
		/// <param name="isMainMenu">Up, Down and Enter keys are tied to menu navigation</param>
		/// <returns></returns>
		public Menu AddMenu(string name, Vector2 position, string[] entries, string theme, bool isMainMenu = false)
		{
			Theme.Menu menuTheme = (Theme.Menu)Themes.GetTheme(theme);
			return controls.AddMenu(InputHandler, name, position, entries, menuTheme, isMainMenu);
		}

		public TextBox AddTextBox(string name, Vector2 position, int width, string theme)
		{
			Theme.TextBox textBoxTheme = (Theme.TextBox)Themes.GetTheme(theme);
			return controls.AddTextBox(InputHandler, name, position, width, textBoxTheme);
		}

		public TextBox AddTextBox(string name, Vector2 position, Vector2 size, string theme)
		{
			Theme.TextBox textBoxTheme = (Theme.TextBox)Themes.GetTheme(theme);
			return controls.AddTextBox(InputHandler, name, position, size, textBoxTheme);
		}

		/// <summary>
		/// Adds an image multiple times as tiles in an area
		/// </summary>
		/// <param name="name">The unique system name of the control</param>
		/// <param name="position">Position in the window</param>
		/// <param name="image">The image to tile</param>
		/// <param name="area">The size of the area to tile in, in pixels</param>
		/// <returns></returns>
		public TileImage AddTileImage(string name, Vector2 position, Texture2D image, Vector2 area)
		{
			return controls.AddTileImage(name, position, image, area);
		}
		#endregion

		#region ControlHandling
		/// <summary>
		/// Returns the named control
		/// </summary>
		/// <param name="controlName">The system name of the control</param>
		/// <returns></returns>
		public Control GetControl(string controlName)
		{
			return controls.GetControl(controlName);
		}

		/// <summary>
		/// Removes a control from the screen
		/// </summary>
		/// <param name="controlName">The system name of the control</param>
		/// <returns></returns>
		public bool RemoveControl(string controlName)
		{
			return controls.RemoveControl(controlName);
		}
		#endregion

		#region Inherited Methods
		internal override void Draw(SpriteBatch spriteBatch)
		{
			throw new NotImplementedException();
		}

		internal virtual void Draw(GraphicsDevice graphics, SpriteBatch spriteBatch)
		{
			if (Visible)
			{
				DrawControls(graphics, spriteBatch);
				DrawSubWindows(graphics, spriteBatch);
			}
		}

		protected void DrawControls(GraphicsDevice graphics, SpriteBatch spriteBatch)
		{
			DrawArea viewport = new DrawArea(graphics, this.DrawArea);
			spriteBatch.Begin();

			controls.Draw(spriteBatch);

			spriteBatch.End();
			viewport.End();
		}

		protected void DrawSubWindows(GraphicsDevice graphics, SpriteBatch spriteBatch)
		{
			foreach (Container subwindow in subwindows)
			{
				subwindow.Draw(graphics, spriteBatch);
			}
		}

		internal override bool OnLeftClick(MouseEventArgs e)
		{
			if (IsMovedOver(e))
			{
				if (Enabled)
				{
					foreach (Container subwindow in subwindows)
					{
						bool actionPerformed = subwindow.OnLeftClick(e);
						if (actionPerformed)
						{
							return true;
						}
					}
					for (int i = controls.Count - 1; i > -1; i--)
					{
						Control foremostControl = controls[i];
						bool actionPerformed = foremostControl.OnLeftClick(e);
						if (actionPerformed)
						{
							return true;
						}
					}
				}
				return true; //Even if no control was activated, the window was still clicked on
			}
			return false;
		}

		internal override bool OnRightClick(MouseEventArgs e)
		{
			if (IsMovedOver(e))
			{
				if (Enabled)
				{
					foreach (Container subwindow in subwindows)
					{
						bool actionPerformed = subwindow.OnRightClick(e);
						if (actionPerformed)
						{
							return true;
						}
					}
					for (int i = controls.Count - 1; i > -1; i--)
					{
						Control foremostControl = controls[i];
						bool actionPerformed = foremostControl.OnRightClick(e);
						if (actionPerformed)
						{
							return true;
						}
					}
				}
				return true; //Even if no control was activated, the window was still clicked on
			}
			return false;
		}

		internal override bool OnMouseMoved(MouseEventArgs e)
		{
			if (IsMovedOver(e))
			{
				if (Enabled)
				{
					foreach (Container subwindow in subwindows)
					{
						bool actionPerformed = subwindow.OnMouseMoved(e);
						if (actionPerformed)
						{
							return true;
						}
					}
					for (int i = controls.Count - 1; i > -1; i--)
					{
						Control foremostControl = controls[i];
						bool actionPerformed = foremostControl.OnMouseMoved(e);
						if (actionPerformed)
						{
							return true;
						}
					}
				}
				return true; //Even if no control was activated, the window was still moved over
			}
			return false;
		}

		internal override bool OnMouseLeftDragged(MouseEventArgs e)
		{
			if (IsMovedOver(e))
			{
				if (Enabled)
				{
					foreach (Container subwindow in subwindows)
					{
						bool actionPerformed = subwindow.OnMouseLeftDragged(e);
						if (actionPerformed)
						{
							return true;
						}
					}
					for (int i = controls.Count - 1; i > -1; i--)
					{
						Control foremostControl = controls[i];
						bool actionPerformed = foremostControl.OnMouseLeftDragged(e);
						if (actionPerformed)
						{
							return true;
						}
					}
				}
				return true; //Even if no control was activated, the window was still moved over
			}
			return false;
		}

		private bool IsMovedOver(MouseEventArgs e)
		{
			return IsWithinBounds(e.Position) && Visible;
		}
		#endregion
	}
}
